	/* Copyright (C) 1999-2005 by Peter Eastman

   This program is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 2 of the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful, but WITHOUT ANY 
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
   PARTICULAR PURPOSE.  See the GNU General Public License for more details. */

package artofillusion;

import artofillusion.animation.*;
import artofillusion.math.*;
import artofillusion.object.*;
import artofillusion.ui.*;
import artofillusion.texture.*;
import buoy.event.*;
import java.awt.*;
import java.io.*;
import java.util.Vector;
import artofillusion.object.TriangleMesh.Edge;
import artofillusion.object.TriangleMesh.Vertex;
import artofillusion.object.TriangleMesh.Face;
/** CreateOLDTool is an EditingTool used for creating Curve objects. */

public class CreateOLDTool extends EditingTool
{
  Image icon, selectedIcon;
  static int counter = 1;
  Point lastPoint;
  Vector clickPoint, smoothness;
  int smoothing;
  CurveF3d theCurve, lastF3dCreated;
  CoordinateSystem coords;
  Scene scene;
  LayoutWindow lw;
  Vector PointList, ObjectsList;
  int NumberOfPoints = 0, AddedPoints = 0;
  
  public static final int HANDLE_SIZE = 3;

  public CreateOLDTool(LayoutWindow fr)
  {
    super(fr);
    lw = fr;
    icon = loadImage("Fast3d.gif");
    selectedIcon = loadImage("selected/Fast3d.gif");
    //qui devo caricare gli oggetti nella scena
    scene = theWindow.getScene();
    PointList = new Vector();
    ObjectsList = new Vector();
  }

  public void activate()
  {
    super.activate();
    theWindow.setHelpText(Translate.text("CreateOLDTool.helpText"));
  }

  public void deactivate()
  {
    //addToScene();
  }

  public int whichClicks()
  {
    return ALL_CLICKS;
  }

  public Image getIcon()
  {
    return icon;
  }

  public Image getSelectedIcon()
  {
    return selectedIcon;
  }

  public String getToolTipText()
  {
    return Translate.text("CreateOLDTool.tipText");
  }

  public boolean hilightSelection()
  {
    return (clickPoint == null);
  }
  
  //called after mouseReleased
  public void drawOverlay(Graphics g, ViewerCanvas view)
  {
    Scene theScene = ((LayoutWindow) theWindow).getScene();
    Camera cam = view.getCamera();
    Point p1, p2;
    int i;

    /*if (clickPoint == null)
      return;*/
    g.setPaintMode();
    g.setColor(Color.black);
    if (theCurve != null)
      Object3D.draw(g, cam, theCurve.getWireframeMesh(), theCurve.getBounds());
    if(mesh != null)
      Object3D.draw(g, cam, mesh.getWireframeMesh(), mesh.getBounds());
    g.setColor(Color.red);
    /*for (i = 0; i < clickPoint.size(); i++)
      {
        p1 = (Point) clickPoint.elementAt(i);
        g.fillRect(p1.x-HANDLE_SIZE/2, p1.y-HANDLE_SIZE/2, HANDLE_SIZE, HANDLE_SIZE);
      }*/
  }
  
  public void mousePressed(WidgetMouseEvent e, ViewerCanvas view)
  {
    Graphics g = view.getComponent().getGraphics();
    Point p;
    
    lastPoint = e.getPoint();
    g.drawOval(e.getX(), e.getY(), 2, 2); //it could be improved saving the coordinates into variables
    AddCurvePoint(lastPoint);
    NumberOfPoints++;
    AddedPoints++;
    g.dispose();
  }
  
  public void mouseDragged(WidgetMouseEvent e, ViewerCanvas view)
  {
    Graphics g = view.getComponent().getGraphics();
    Point p;
    
    lastPoint = e.getPoint();
    g.drawOval(e.getX(), e.getY(), 1, 1);
    if ( (NumberOfPoints % 5) == 0)
    {
        AddCurvePoint(lastPoint);
        AddedPoints++;	
    }
    NumberOfPoints++;
    g.dispose();
  }

  public void mouseReleased(WidgetMouseEvent e, ViewerCanvas view)
  {	
  	Graphics g = view.getComponent().getGraphics();
	Camera cam = view.getCamera();
	Point p, dragPoint = e.getPoint();
	Vec3 vertex[], orig, ydir, zdir;
	float s[];
	int i;	

   /*
    if (e.getClickCount() != 2)
      {
        clickPoint.addElement(dragPoint);
        smoothness.addElement(new Float(e.isShiftDown() ? 0.0f : 1.0f));
      }
   */
    //if (clickPoint.size() > 1)
      //{
        // Create a new line object.  First, find all the points in world coordinates.
            
	vertex = new Vec3 [AddedPoints];
	s = new float [AddedPoints];
	orig = new Vec3();
	for (i = 0; i < PointList.size(); i++)
		{
		Point punto = (Point) PointList.elementAt(i);
		vertex[i] = cam.convertScreenToWorld(punto, ModellingApp.DIST_TO_SCREEN);
		s[i] = (float)2;
		orig = orig.plus(vertex[i]);
		}
	orig = orig.times(1.0/vertex.length);
	
	// Find the object's coordinate system.
	
	ydir = cam.getViewToWorld().timesDirection(Vec3.vy());
	zdir = cam.getViewToWorld().timesDirection(new Vec3(0.0, 0.0, -1.0));
	coords = new CoordinateSystem(orig, zdir, ydir);
		
	// Transform all of the vertices into the object's coordinate system.
		
	for (i = 0; i < PointList.size(); i++)
		{
		vertex[i] = coords.toLocal().times(vertex[i]);
		vertex[i].z = 0.0;  // Prevent round-off error.
		}
	
	theCurve = new CurveF3d(vertex, /*s, smoothing,*/ true);
	setLastF3dCreated(theCurve);
	cam.setObjectTransform(coords.fromLocal());
	
	//lw.executeScript(new File("/root/AoIsrc20/Tools/src/artofillusion/tools/root/AoIsrc20/Scripts/Tools/Sculpt.bsh")); 
	script();
	addToScene();
	/*if (e.getClickCount() == 2)
		{*/
	//theCurve.setClosed(e.isControlDown());
	
	//g.dispose();
		/*return;
		}*/
	theWindow.updateImage();
	//view.repaint();
	//view.draw(g);
	//drawOverlay(g, view);
	PointList.clear();
	AddedPoints = 0;
	NumberOfPoints = 0;
    g.dispose();
  }  
  /** When the user presses Enter, add the curve to the scene. */
  
  //public void MouseMoved() ...
  
  public void keyPressed(KeyPressedEvent e, ViewerCanvas view)
  {
    if (e.getKeyCode() == KeyPressedEvent.VK_ENTER && theCurve != null)
      {
        theCurve.setClosed(e.isControlDown());
        addToScene();
      }
  }

  /** Add the curve to the scene. */
  
  private void addToScene()
  {
    boolean addCurve = (theCurve != null);
    if (addCurve)
      {
        ObjectInfo info = new ObjectInfo(mesh, coords, "CurveF3d "+(counter++));
        info.addTrack(new PositionTrack(info), 0);
        info.addTrack(new RotationTrack(info), 1);
        UndoRecord undo = new UndoRecord(theWindow, false);
        undo.addCommandAtBeginning(UndoRecord.SET_SCENE_SELECTION, new Object [] {theWindow.getScene().getSelection()});
        ((LayoutWindow) theWindow).addObject(info, undo);
        theWindow.setUndoRecord(undo);
        ((LayoutWindow) theWindow).setSelection(theWindow.getScene().getNumObjects()-1);
        //lw.setLastFast3dInfo(info);
      }
    clickPoint = null;
    smoothness = null;
    //theCurve = null;
    coords = null;
    if (addCurve)
      theWindow.updateImage();
  }
  
  /*
  template funzioni per tradurre il codice java in un linguaggio piu- semplice
  
  */
  
  public void AddCurvePoint(Point p)
  {
    PointList.add(p);
  }
  
  public void setLastF3dCreated(CurveF3d f3d)
  {
  	//lw.setLastF3dCreated(f3d);
  }
  
  public CurveF3d getLastF3dCreated()
  {
  	//return lw.getLastF3dCreated();
  	return null;
  }
  
  public void splitLoop()
  {
  	int i;
  	for (i = 0; i < edge.length; i++)
  	  	split[i] = (edge[i].f2 != -1 && onBoundary[edge[i].v1] && onBoundary[edge[i].v2]);
  }
  
  public void vertEdgeDist_java()
  {
  	int i;
  	dist2 = new double[vert.length];
  	for (i = 0; i < vert.length; i++)
    {
      if (onBoundary[i])
        continue;
      dist2[i] = Double.MAX_VALUE;
      int j;
      for (j = 0; j < edge.length; j++)
        if (edge[j].f2 == -1)
        {
          double d2 = vertEdgeDistance2(vert[i].r, vert[edge[j].v1].r, vert[edge[j].v2].r);
          if (d2 < dist2[i])
            dist2[i] = d2;
        }
    }
  }
  
  public double vertEdgeDistance2(Vec3 point, Vec3 end1, Vec3 end2)
  {
    Vec3 v = end2.minus(end1);
    double len = v.length();
    if (len < 1e-10)
      return point.distance2(end1);
    v.scale(1.0/len);
    Vec3 v1 = point.minus(end1);
    double dot = v.dot(v1);
    if (dot > len)
      return end2.distance2(point);
    if (dot < 0.0)
      return end1.distance2(point);
    v1.subtract(v.times(v.dot(v1)));
    return v1.length2();
  }
  
  public void findMaxLocalDistance()
  {
    maxDist2 = new double [dist2.length];
    int i;
    for (i = 0; i < maxDist2.length; i++)
      maxDist2[i] = dist2[i];
    for (i = 0; i < edge.length; i++)
    {
      if (dist2[edge[i].v1] > maxDist2[edge[i].v2])
        maxDist2[edge[i].v2] = dist2[edge[i].v1];
      if (dist2[edge[i].v2] > maxDist2[edge[i].v1])
        maxDist2[edge[i].v1] = dist2[edge[i].v2];
    }
  }

  /*public int[] initPairIndex(int pairIndex[], int numVert, boolean onBoundary[])
  {
  	int i;
  	for (i = 0; i < onBoundary.length; i++)
  	    if (!onBoundary[i])
  	      pairIndex[i] = numVert++;
  	
  	return pairIndex;
  }*/
  
  public void initFaces()
  {
  	face = mesh.getFaces();
    newFace = new int [face.length*2][3];
    int i;
    for (i = 0; i < face.length; i++)
    {
  	int j = face.length+i;
  	newFace[i][0] = face[i].v1;
  	newFace[i][1] = face[i].v2;
  	newFace[i][2] = face[i].v3;
  	newFace[j][0] = (onBoundary[face[i].v1] ? face[i].v1 : pairIndex[face[i].v1]);
  	newFace[j][1] = (onBoundary[face[i].v3] ? face[i].v3 : pairIndex[face[i].v3]);
  	newFace[j][2] = (onBoundary[face[i].v2] ? face[i].v2 : pairIndex[face[i].v2]);
    }
  }
  
  public void findBoundaryVerts()
  {
    vert = (Vertex[])mesh.getVertices();
    edge = mesh.getEdges();
    onBoundary = new boolean [vert.length];
    int i;
    for (i = 0; i < edge.length; i++)
      if (edge[i].f2 == -1)
        onBoundary[edge[i].v1] = onBoundary[edge[i].v2] = true;
  }
  
  public void subd()
  {
  	//TriangleMesh mesh = theCurve.convertToTriangleMesh(0.1); //scope problem... mesh is local and cannot be passed as return value
    Texture tex = scene.getDefaultTexture();
    mesh.setTexture(tex, tex.getDefaultMapping());
    mesh = TriangleMesh.subdivideEdges(mesh, null, Double.MAX_VALUE);
    mesh = TriangleMesh.subdivideEdges(mesh, null, Double.MAX_VALUE);
  }

public void lastFunc()
{
	  norm = mesh.getNormals();
	    newVert = new TriangleMesh.Vertex [numVert];
	    for (int i = 0; i < vert.length; i++)
	    {
	      newVert[i] = vert[i];
	      if (!onBoundary[i])
	      {
	        newVert[pairIndex[i]] = mesh.new Vertex(vert[i]);
	        double displace = Math.sqrt(2.0*Math.sqrt(dist2[i]*maxDist2[i])-dist2[i]);
	        newVert[i].r.add(norm[i].times(displace));
	        newVert[pairIndex[i]].r.subtract(norm[i].times(displace));
	      }
	    }
}


	public void script()
	{    
		mesh = theCurve.convertToTriangleMesh(0.1);
		subd();
		findBoundaryVerts();
		edge = mesh.getEdges();
		split = new boolean [edge.length];
		splitLoop();
		mesh = TriangleMesh.subdivideLinear(mesh, split);
		findBoundaryVerts();
		vert = (Vertex[])mesh.getVertices();
		edge = mesh.getEdges();
		vertEdgeDist_java();
		findMaxLocalDistance();
		pairIndex = new int [vert.length];
		numVert = vert.length;
		for (int i = 0; i < onBoundary.length; i++)
		  if (!onBoundary[i])
		    pairIndex[i] = numVert++;
		initFaces();
		lastFunc();
		mesh = new TriangleMesh(newVert, newFace);
		//lw.addObject(mesh, coords, "Sculpted ", null);
		//lw.setSelection(scene.getNumObjects()-1);
	}
	
	TriangleMesh mesh;
	boolean onBoundary[];
	Edge edge[];
	boolean split[];
	Vertex vert[], newVert[];;
	double dist2[];
	double maxDist2[];
	int pairIndex[];
	int numVert;
	int newFace[][];
	Vec3 norm[] = new Vec3[3];
	Face face[];
} 
